{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "参考 [pyg文档](https://pytorch-geometric.readthedocs.io/en/latest/get_started/introduction.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 定义基本结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch_geometric.data import Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "edge_index 表明哪些节点朝哪些方向连接, 两个节点单向连接只需要写一组, 双向连接需要写两组，需要注意的是，无向图实际上是指节点都通过双向边连接，而不是只连接而没有方向。\n",
    "\n",
    "![](https://pytorch-geometric.readthedocs.io/en/latest/_images/graph.svg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [],
   "source": [
    "edge_index = torch.tensor([[0, 1, 1, 2],\n",
    "                           [1, 0, 2, 1]], dtype=torch.long)\n",
    "\n",
    "# 节点特征矩阵, 特征可以是任意维度张量, 但是 shape 要统一, 节点特征数可以多于节点数\n",
    "x = torch.tensor([[-1], [0], [1]], dtype=torch.float)\n",
    "\n",
    "data = Data(x=x, edge_index=edge_index)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "edge_index 在定义时 shape 是 [2, \\*], 如果定义为 [\\*, 2] 则需要转置输入Data, 转置后还需深拷贝 `contiguous()`\n",
    "\n",
    "```python\n",
    "edge_index = torch.tensor([[0, 1],\n",
    "                           [1, 0],\n",
    "                           [1, 2],\n",
    "                           [2, 1]], dtype=torch.long)\n",
    "x = torch.tensor([[-1], [0], [1]], dtype=torch.float)\n",
    "\n",
    "data = Data(x=x, edge_index=edge_index.t().contiguous())\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`edge_index()` 验证 data 是否有问题, 比如 edge_index 中连接了 4 个节点,  x 中就应该依次包括大于等于 4 个节点的特征."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.validate(raise_on_error=True)  # 验证数据是否有问题"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# data 属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.num_nodes  # 节点数, 如果存在孤立节点, 结果不一定正确, 可以手动指定"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.num_edges  # 边数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.num_node_features  # 节点特征数, 但是对高维特征识别似乎不正确"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.has_isolated_nodes()  # 是否有孤立节点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.has_self_loops()  # 是否有自环边"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.is_directed()  # 是否有向图"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 标准数据集"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ENZYMES 蛋白酶数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch_geometric.datasets import TUDataset\n",
    "\n",
    "dataset = TUDataset(\"\", name='ENZYMES')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "600"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(dataset)  # 一共有600个图，每个图中包含若干节点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset.num_classes  # 一共有6个酶类别"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset.num_node_labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset.num_node_features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = dataset[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.is_undirected()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 划分数据集\n",
    "\n",
    "这里就手动划分为9:1了，600*0.9=540"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset = dataset.shuffle()  # 打乱数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_dataset = dataset[:540]\n",
    "test_dataset = dataset[540:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 图卷积神经网络"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cora 是引文数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch_geometric.datasets import Planetoid\n",
    "\n",
    "dataset = Planetoid(\"\", name='Cora')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1433"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset.num_node_features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset.num_classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn.functional as F\n",
    "from torch_geometric.nn import GCNConv\n",
    "\n",
    "class GCN(torch.nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.conv1 = GCNConv(dataset.num_node_features, 16)\n",
    "        self.conv2 = GCNConv(16, dataset.num_classes)\n",
    "\n",
    "    def forward(self, data):\n",
    "        x, edge_index = data.x, data.edge_index\n",
    "\n",
    "        x = self.conv1(x, edge_index)\n",
    "        x = F.relu(x)\n",
    "        x = F.dropout(x, training=self.training)\n",
    "        x = self.conv2(x, edge_index)\n",
    "\n",
    "        return F.log_softmax(x, dim=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "model = GCN().to(device)\n",
    "data = dataset[0].to(device)\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)\n",
    "\n",
    "model.train()\n",
    "for epoch in range(200):\n",
    "    optimizer.zero_grad()\n",
    "    out = model(data)\n",
    "    loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])\n",
    "    loss.backward()\n",
    "    optimizer.step()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy: 0.8070\n"
     ]
    }
   ],
   "source": [
    "model.eval()\n",
    "pred = model(data).argmax(dim=1)\n",
    "correct = (pred[data.test_mask] == data.y[data.test_mask]).sum()\n",
    "acc = int(correct) / int(data.test_mask.sum())\n",
    "print(f'Accuracy: {acc:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可视化图，用networkx包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAApQAAAHzCAYAAACe1o1DAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAifUlEQVR4nO3de5SddX3v8e+eyYXMBBIlBmihQixoqRWxSisFT+vxaLXKEqnXysErtvVghHrUesPL8VKLgJbjra1KXS68a0WPcqzWO3pQIaiIokQEgQQCScgMZK7nj5AhIbP37D3Pvjzf53m91upaJDvz7KfLX2Y+mZn9nsbs7OxsAADAIg0N+gYAAMjNoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBACjEoAQAoBCDEgCAQgxKAAAKMSgBAChkyaBvAFi8qZmZ2DExHTOzszHUaMTKZcOxZMi/E8nB+YXqMCghme07J2Pj1vG4eWxnjE1O7/P46NLhOHh0eRyxeiQOWL50AHcIzTm/UE2N2dnZ2UHfBLCwsYmpuHzTttg8PhGNiGj1F3f342tHlsWxB62K0WX+7chgOb9QbQYlJLBx63hs2LwtZmdbfyC+t0ZENBoRx6xdFUesHunV7UFLzi9Un0EJJXf1ljviqlt3FL7O0WtWxoMO3L8LdwTtc36hHnz3M5TYxq3jXflgHBFx1a074ldbx7tyLWiH8wv14RtToKTGJqZiw+Zt+/z+T3/wvfjm5z8bP9/wg7ht080xtn173Od+a+P+D/y9OPmFL44HPey4pte8YvO2uN/IMt+TRs81O7/zedMLnhVXfOtrc79+5//5ehy67sh9/pzzC+XlM5RQUpdv2vU9Z/f2jc99Oi656MLYeNWPY9uWW2NqciJuufGG+P5/fjle/awnx5c//pGm15yd3XVd6LVm5/fevvqpj+41JltxfqG8DEoooe07J2Pz+MS8L2BoDA3FIx/3xHjdv14UH/nhL+L9X/9BPPJxT5x7/CPnviWmp/fNsUTsekHE5vGJ2L5zsjc3DtH6/O7ptk03x4f+4Q0xNDQUy5bvt+B1nV8oL4MSSmjj1vFoNHns2X/3qnjZO98fx/zJf4n9RkbiwIMOiRee/da5x+/Yentsv21L02s37r4+9Eqr87un973+FTG2fVs88Tmnx6oD17R1becXysmghBK6eWxn08/ujKzc95WuO++8c+6/l69YEfuvvk/Ta8/efX3olVbnd7dvXPzp+P5/fjl+6/B18YyX/M+2r+38QjkZlFAykzMz8/4EkWZmZ2fj397+xrlf/7ennRpLlrb+CSNjk9MxNTOz6HuEZto5v1tvvSU+8ObXxtDQULz4LefG8v1WdPQczi+Uj0EJJTM20f6YnJyYiHe9/Iy49JLPR0TEH/zxCfHsv/v7tt52RwfPA+1q5/z+8xtfFXdsvT2ecOrzW1YJWnF+oVy0F6BkZtr8WQPjO+6It/+P58ePvvutiIh4xKMfG2ee+55Yumx5V58HOrHQufrFjzbEd//vF2L0gFVx3GMeH7/40YaIiJiavOeFNtdf8/OYnZmNw373qEU/D9BfBiWUzFBj4ZczbNl0U7z59FPjup9dFRERf/6s58TzXv2mGB4e7urzQKcWOld3jY9FRMTY9m3xulOfMu+fOWf9C+PwBx0d7/jsfyz6eYD+MiihZFYuaz0Kf/3zq+N/nf5XseXmm6LRaMSzX/bqePLz/7brzwOL0a9z5fxCufhZ3lBCl1y7uekLG/7plS+Nr3324y3f/g0XfjIe/EfHN318dOlwPG7d2kL3CM20Or/N/PWjj4tbbrwhIpr/pJzdnF8oHy/KgRI6eHR5Wx2/xWjcfX3oFecX6sdnKKGEtu+cjP/41a09u/5jDl8TByxvnRaCxXJ+oX58hhJK6IDlS2PtyLKuf5anERFrR5b5YExPOb9QPwYllNSxB62Kbr+QtdHYdV3oNecX6sWghJIaXbYkjlnb3Q+eD127KkaXiTvQe84v1ItBCSV2xOqROHrNyq5c6+g1+8fhq0e6ci1oh/ML9eFFOZDAxq3jsWHztpidjejkL2wjdn2Z8KFrV/lgzMA4v1B9BiUkMTYxFZdv2habxyeiEa0/MM9MT8fQ8HDcb8XSeNjBq32ZkIHr5PzOzs5EozEUK6Z3xqN+91DnFxLwJW9IYnTZkjjhsAPjMYeviXWrR2J06fw/KWR06XAcMDUeL3nCo2LLD7/lgzGl0Mn5XbdqJN76/KfHx//xDc4vJOEzlJDY1MxMXHblj+O0054TF174oXjEQx4cS4Z2/TvxxBNPjEajEd/4xjcGfJcwv6mZmdgxMR0zs7Mx1GjEymXDc+f3nHPOiVe96lVx3XXXxSGHHDLgOwUW4jOUkNiSoaFYPjMV11x5eSyfmZr7YBwRcdZZZ8U3v/nNuOyyywZ4h9DckqGhWL3f0rjvimWxer+le53fF7zgBbF8+fJ497vfPcA7BNplUEJFnXTSSbFu3bo477zzBn0r0LHVq1fH8573vHjPe94Td95556BvB1iAQQkVNTw8HOvXr49PfOITcf311w/6dqBj69evj9tuuy0+/OEPD/pWgAUYlFBhz33uc2N0dDQuuOCCQd8KdGzdunVx8sknx3nnnRczMzODvh2gBYMSKmz//feP008/Pd73vvfFjh07Bn070LGzzjorrr766vjSl7406FsBWjAooeLOOOOM2LFjR3zwgx8c9K1Ax44//vg47rjj4txzzx30rQAtGJRQcYcddlg89alPjfPPPz+mp6cHfTvQkUajEWeeeWZ85StfiQ0bNgz6doAmDEqogTPPPDOuvfbauPjiiwd9K9CxU045JQ477LA4//zzB30rQBMGJdTAcccdFyeccIIvG5LS0qVL4yUveUl85CMfiZtuumnQtwPMw6CEmhA6JzOhcyg3gxJqQuiczITOodwMSqgJoXOyEzqH8jIooUaEzslM6BzKy6CEGhE6JzuhcygngxJqRuiczITOoZwMSqgZoXMyEzqHcjIooYaEzslM6BzKx6CEGhI6JzOhcygfgxJqSuiczITOoVwMSqgpoXMyEzqHcjEooaaEzslO6BzKw6CEGhM6JzOhcygPgxJqTOic7ITOoRwMSqg5oXMyEzqHcjAooeaEzslM6BzKwaAEhM5JTegcBs+gBITOSU3oHAbPoAQiQuic3ITOYbAMSiAihM7JTegcBsugBCJC6Jz8hM5hcAxKYI7QOZkJncPgGJTAHKFzshM6h8EwKIG9CJ2TmdA5DIZBCexF6JzMhM5hMAxKYB9C52QmdA79Z1AC+xA6JzOhc+g/gxKYl9A5mQmdQ38ZlMC8hM7JTOgc+sugBOYldE52QufQPwYl0JTQOZkJnUP/GJRAU0LnZCd0Dv1hUAItCZ2TmdA59IdBCbQkdE5mQufQHwYlsCChczITOofeMyiBBQmdk5nQOfSeQQm0ReiczITOobcMSqAtQudkJnQOvWVQAm0ROic7oXPoHYMSaJvQOZkJnUPvGJRA24TOyU7oHHrDoAQ6InROZkLn0BsGJdARoXMyEzqH3jAogY4JnZOZ0Dl0n0EJdEzonMyEzqH7DEpgUYTOyUzoHLrLoAQWReiczITOobsMSmBRhM7JTugcusegBBZN6JzMhM6hewxKYNGEzslO6By6w6AEChE6JzOhc+gOgxIoROiczITOoTsMSqAwoXMyEzqH4gxKoDChczITOofiDEqgK4TOyUzoHIoxKIGuEDonM6FzKMagBLpC6JzshM5h8QxKoGuEzslM6BwWz6AEukbonOyEzmFxDEqgq4TOyUzoHBbHoAS6SuiczITOYXEMSqDrhM7JTOgcOmdQAl0ndE5mQufQOYMS6AmhczITOofOGJRATwidk5nQOXTGoAR6Quic7ITOoX0GJdAzQudkJnQO7TMogZ4ROic7oXNoj0EJ9JTQOZkJnUN7DEqgp4TOyUzoHNpjUAI9J3ROZkLnsDCDEug5oXMyEzqHhRmUQF8InZOZ0Dm0ZlACfSF0TmZC59CaQQn0hdA52QmdQ3MGJdA3QudkJnQOzRmUQN8InZOd0DnMz6AE+kronMyEzmF+BiXQV0LnZCZ0DvMzKIG+EzonM6Fz2JdBCfSd0DmZCZ3DvgxKYCCEzslM6Bz2ZlACAyF0TmZC57A3gxIYCKFzshM6h3sYlMDACJ2TmdA53MOgBAZG6JzshM5hF4MSGCihczITOoddDEpgoITOyUzoHHYxKIGBEzonM6FzMCiBEhA6JzOhczAogZIQOiczoXPqzqAESkHonMyEzqk7gxIoBaFzshM6p84MSqA0hM7JTOicOjMogdIQOic7oXPqyqAESkXonMyEzqkrgxIoFaFzMhM6p64MSqB0hM7JTOicOjIogdIROiczoXPqyKAESknonMyEzqkbgxIoJaFzMhM6p24MSqCUhM7JTuicOjEogdISOiczoXPqxKAESkvonOyEzqkLgxIoNaFzMhM6py4MSqDUhM7JTOicujAogdITOiczoXPqwKAESk/onMyEzqkDgxJIQeiczITOqTqDEkhB6JzMhM6pOoMSSEHonOyEzqkygxJIQ+iczITOqTKDEkhD6JzshM6pKoMSSEXonMyEzqkqgxJIReiczITOqSqDEkhH6JzMhM6pIoMSSEfonMyEzqkigxJISeiczITOqRqDEkhJ6JzMhM6pGoMSSEnonOyEzqkSgxJIS+iczITOqRKDEkhL6JzshM6pCoMSSE3onMyEzqkKgxJITeiczITOqQqDEkhP6JzMhM6pAoMSSE/onMyEzqkCgxKoBKFzMhM6JzuDEqgEoXMyEzonO4MSqAShc7ITOiczgxKoDKFzMhM6JzODEqgMoXOyEzonK4MSqBShczITOicrgxKoFKFzMhM6JyuDEqgcoXMyEzonI4MSqByhczITOicjgxKoJKFzMhM6JxuDEqgkoXMyEzonG4MSqCShc7ITOicTgxKoLKFzMhM6JxODEqgsoXOyEzonC4MSqDShczITOicLgxKoNKFzMhM6JwuDEqg8oXMyEzonA4MSqDyhczITOicDgxKoBaFzMhM6p+wMSqAWhM7JTOicsjMogVoQOic7oXPKzKAEakPonMyEzikzgxKoDaFzshM6p6wMSqBWhM7JTOicsjIogVoROiczoXPKyqAEakfonMyEzikjgxKoHaFzMhM6p4wMSqCWhM7JTOicsjEogVoSOiczoXPKxqAEaknonOyEzikTgxKoLaFzMhM6p0wMSqC2hM7JTuicsjAogVoTOiczoXPKwqAEak3onMyEzikLgxKoPaFzMhM6pwwMSqD2hM7JTOicMjAoAULonNyEzhk0gxIghM7JTeicQTMoAULonPyEzhkkgxLgbkLnZCZ0ziAZlAB3EzonO6FzBsWgBNiD0DmZCZ0zKAYlwB6EzslM6JxBMSgB7kXonMyEzhkEgxLgXoTOyUzonEEwKAHmIXROZkLn9JtBCTAPoXMyEzqn3wxKgHkInZOd0Dn9ZFACNCF0TmZC5/STQQnQhNA52Qmd0y8GJUALQudkJnROvxiUAC0InZOZ0Dn9YlACLEDonMyEzukHgxJgAULnZCZ0Tj8YlABtEDonM6Fzes2gBGiD0DmZCZ3TawYlQBuEzslO6JxeMigB2iR0TmZC5/SSQQnQJqFzshM6p1cMSoAOCJ2TmdA5vWJQAnRA6JzMhM7pFYMSoENC52QmdE4vGJQAHRI6JzOhc3rBoARYBKFzMhM6p9sMSoBFEDonM6Fzus2gBFgEoXOyEzqnmwxKgEUSOiczoXO6yaAEWCShc7ITOqdbDEqAAoTOyUzonG4xKAEKEDonM6FzusWgBChI6JzMhM7pBoMSoCChczITOqcbDEqALhA6JzOhc4oyKAG6QOiczITOKcqgBOgCoXOyEzqnCIMSoEuEzslM6JwiDEqALhE6JzuhcxbLoAToIqFzMhM6Z7EMSoAuEjonM6FzFsugBOgyoXMyEzpnMQxKgC4TOiczoXMWw6AE6AGhczITOqdTBiVADwidk5nQOZ0yKAF6QOic7ITO6YRBCdAjQudkJnROJwxKgB4ROic7oXPaZVAC9JDQOZkJndMugxKgh4TOyUzonHYZlAA9JnROZkLntMOgBOgxoXMyEzqnHQYlQB8InZOZ0DkLMSgB+kDonMyEzlmIQQnQB0LnZCd0TisGJUCfCJ2TmdA5rRiUAH0idE52Quc0Y1AC9JHQOZkJndOMQQnQR0LnZCZ0TjMGJUCfCZ2TmdA58zEoAfpM6JzMhM6Zj0EJMABC52QmdM69GZQAAyB0TmZC59ybQQkwAELnZCd0zp4MSoABETonM6Fz9mRQAgyI0DnZCZ2zm0EJMEBC52QmdM5uBiXAAAmdk5nQObsZlAADJnROZkLnRBiUAAMndE5mQudEGJQApSB0TmZC5xiUACUgdE5mQucYlAAlIHROdkLn9WZQApSE0DmZCZ3Xm0EJUBJC52QndF5fBiVAiQidk5nQeX0ZlAAlInROZkLn9WVQApSM0DmZCZ3Xk0EJUDJC52QmdF5PBiVACQmdk5nQef0YlAAlJHROZkLn9WNQApSQ0DnZCZ3Xi0EJUFJC52QmdF4vBiVASQmdk53QeX0YlAAlJnROZkLn9WFQApSY0DmZCZ3Xh0EJUHJC52QmdF4PBiVAyQmdk5nQeT0YlAAJCJ2TmdB59RmUAAkInZOZ0Hn1GZQACQidk53QebUZlABJCJ2TmdB5tRmUAEkInZOd0Hl1GZQAiQidk5nQeXUZlACJCJ2TmdB5dRmUAMkInZOZ0Hk1GZQAyQidk5nQeTUZlAAJCZ2TmdB59RiUAAkJnZOZ0Hn1GJQACQmdk53QebUYlABJCZ2TmdB5tRiUAEkJnZOd0Hl1GJQAiQmdk5nQeXUYlACJCZ2TmdB5dRiUAMkJnZOZ0Hk1GJQAyQmdk5nQeTUYlAAVIHROZkLn+RmUABUgdE5mQuf5GZQAFSB0TnZC57kZlAAVIXROZkLnuRmUABUhdE52Qud5GZQAFSJ0TmZC53kZlAAVInROZkLneRmUABUjdE5mQuc5GZQAFSN0TmZC5zkZlAAVJHROZkLn+RiUABUkdE5mQuf5GJQAFSR0TnZC57kYlAAVJXROZkLnuRiUABUldE52Qud5GJQAFSZ0TmZC53kYlAAVJnROZkLneRiUABUndE5mQuc5GJQAFSd0TmZC5zkYlAA1IHROZkLn5WdQAtSA0DmZCZ2Xn0EJUANC52QndF5uBiVATQidk5nQebkZlAA1IXROdkLn5WVQAtSI0DmZCZ2Xl0EJUCNC52QmdF5eBiVAzQidk5nQeTkZlAA1I3ROZkLn5WRQAtSQ0DmZCZ2Xj0EJUENC52QmdF4+BiVADQmdk53QebkYlAA1JXROZkLn5WJQAtSU0DnZCZ2Xh0EJUGNC52QmdF4eBiVAjQmdk5nQeXkYlAA1J3ROZkLn5WBQAtSc0DmZCZ2Xg0EJgNA5qQmdD55BCYDQOakJnQ+eQQmA0DnpCZ0PlkEJQEQInZOb0PlgGZQARITQOfkJnQ+OQQnAHKFzMhM6HxyDEoA5QudkJnQ+OAYlAHsROiczofPBMCgB2IvQOZkJnQ+GQQnAPoTOyUzovP8MSgD2IXROZkLn/WdQArAPoXOyEzrvL4MSgHkJnZOZ0Hl/GZQAzEvonOyEzvvHoASgKaFzMhM67x+DEoCmhM7JTOi8fwxKAFoSOiczofP+MCgBaEnonMyEzvvDoARgQULnZCZ03nsGJQALEjonM6Hz3jMoAViQ0DnZCZ33lkEJQFuEzslM6Ly3DEoA2iJ0TnZC571jUALQNqFzMhM67x2DEoC2CZ2TmdB57xiUAHRE6JzMhM57w6AEoCNC52QmdN4bBiUAHRM6JzOh8+4zKAHomNA5mQmdd59BCUDHhM7JTui8uwxKABZF6JzMhM67y6AEYFGEzslO6Lx7DEoAFk3onMyEzrvHoARg0YTOyUzovHsMSgAKETonM6Hz7jAoAShE6JzMhM67w6AEoDChczITOi/OoASgMKFzMhM6L86gBKAwoXOyEzovxqAEoCuEzslM6LwYgxKArhA6Jzuh88UzKAHoGqFzMhM6XzyDEoCuETonM6HzxTMoAegqoXMyEzpfHIMSgK4SOiczofPFMSgB6DqhczITOu+cQQlA1wmdk5nQeecMSgC6Tuic7ITOO2NQAtATQudkJnTeGYMSgJ4QOic7ofP2GZQA9IzQOZkJnbfPoASgZ4TOyUzovH0GJQA9JXROZkLn7TEoAegpoXMyEzpvT+0H5dTMTGy9azJuu3Mitt41GVNeyUUiUzMzsXNoSRz5kGNj59AS55fSmi907v0vWcwXOnd+99aYnZ2dHfRN9Nv2nZOxcet43Dy2M8Ym9/2entGlw3Hw6PI4YvVIHLB86QDuEJpzfsloeno6jjrqqHjMX5wUf/vq1zu/pLN+/fr46rcvjQ/9+xfj1p3Tzu+91GpQjk1MxeWbtsXm8YloRESr/8d3P752ZFkce9CqGF22pD83CU04v2Q2NjEV/37Zj2K/NQfHrtPZaPpnnV/KZmxiKr7zq5vjjtnhmJ2ZicZQ8y/w1vX81mZQbtw6Hhs2b4vZ2dYfiO+tERGNRsQxa1fFEatHenV70JLzS2bOL5k5v+2pxaC8essdcdWtxaO6R69ZGQ86cP8u3BG0z/klM+eXzJzf9lX+RTkbt4535TBERFx164741dbxrlwL2uH8kpnzS2bOb2cqPSjHJqZiw+ZtXb3mFZu3xdjEVFevCfNxfsnM+SUz57dzlf5O0cs37fqeh/ncOTYWn/nnC+LSL10ct9z4m1i+YkUceczD4imnnxFHP/yPml5zdnbXdU847MAe3TXs0ur8Tk5MxEff9fa45sor4tqfXBl3ju36V/TvP+KR8cYPf6rpNZ1f+qXZ+b32J1fGJ9/7zrjuZz+N7bdtibvuHI+RlQfE7xz5wHjUSafEY576rGg05n/BjvNLv7R6//vj730nzj7tL5u+7dNefFY8/YyX7fP7VT+/lR2U23dOxubxiXkfu2t8PF576smx8aofz/3e5MTOuPwbX40N3/parP/HC+KEv3jyvG87GxGbxydi+87J2iUB6J9W5zciYuKuO+Oz//Lupo834/zSD63O7/W/vCa+9+Uv7vV7O7bdHld9/7tx1fe/G7+59hfxnFeePe/bOr/0w0Lvfxer6ue3sl/y3rh1vGmU4hPvOW9uTB7/+CfFB77zozj7gx+L5StWxMzMTLzv7FfEHVtvb3rtxt3Xh15pdX4jIoaXLI3HPfO0ePGbz43nv/pNHV3b+aXXWp3fgw+7f7zo9f8QF1zy7bhow7Xx/q//IP70yU+be/wrn7qo5bWdX3ptofe/e3rDhZ+MT119417/N99nJ3er8vmt7KC8eWznvC/vn52dja9+6qNzvz71Za+NVfc9MB7yyBPj+D9/UkREjO+4I779xc81vfbs3deHXml2fnfbb2QkTj/7rfHoU54Rhxy+rqNrO7/0Wqvz+8BjHx6Pfcapccj9j4hly/eLAw86JJ703NPnHl+ytPVnbpxfem2h979FVPn8VnJQTs7MzFuwj4jYdMOvY/ttWyIiYsXoylj724fOPfY7R/3e3H9fc8UPWz7H2OR07X/MEr3R6vx2i/NLr3RyfmdmZuLWm34TF3/w/XO/96TnnN7iLXZxfumVTt//vuPMF8XT/+D+cerDHxivefbJ8c3Pf2bBt6nq+a3k91COTTQ/DNtuvWXuv0cPOGCvx0b2v6cRtXXLLbGQy678cSyfqe4rthiMnUNLIlbcr+fPs2NiOlbvV8l/UzJArd7/7umVT39iXLPhnn+4Dy9ZEqe+7DVtDcoI55feaPf87rb7E1RTk5Px0+9/L376/e/Fr39+dfzVWX/f8u2qeH4rOShn2my179N03+PXzV5luKfTTntOXHPl5R3dGyzkyIccG2/7+Bd6/jzt/j2BTiz2XE1PTcWH3vb6mJ6ajCe/4MU9ex5opZ1ztXrN/eK0l78uHnrin8VBhx4W4zt2xOc++N743AfeGxERn/3Xd8fjnvnfY80hv13oebKp5KAcajEGV6255zM/Y9u37/XY+B133PPnDlz4M0QXXvghn6Gk63YOLYmb+vA8rf6ewGK1e67e9rHPx/T0dGy9dXN85ZMXxcf+6ZyIiLjonW+PP3vKM2LVfVunVZxfeqGdc3XoA46MQx9w5Nyvl68YidNe/rr4f1+5JG6+bmPMTE/HNRsubzkoq3h+KzkoVy4bbvrYwYfdP1YduCa2bbk17hofi82/uWHu+yiv+/nVc3/uyGOOXfB5HvGQB8eSFj8gHhZjamYmPnfNpp4/T6u/J7BYnZyr4eHhOPCgQ+JpLz4rLv7Q+2P8ju0xNTkZm66/bsFB6fzSC+2cq+np6Rge3vfPNfZ4bXhjgW1QxfNbyTW0ZGgoRpc2/x/r0U95xtx/f/icN8X227fElZd+My695OKIiBhZuX/8yeNPavkco0uHjUl6YqHzu9v227fE9tu3xPiOez6zPjU1Off7O+9snqZwfumVhc7vB97yurj0ki/E5huuj8mJibj9ls3xyfe+M8bv2PUVo6Hh4Tjo0N9p+RzOL73Szvvfd7z0RXHRu94ev/rZVTE5sTNuv2VzXPj2N8ZN11276xpLl8YDH/qHTd++que3MbvPNxJWw4ZN2+LarePzvvT/rvHxeM2zn7xX2Hy3oaGhlmHziF0dqXWrR+KYg1Z17X5hT63O726nPOi3Wl6j2U9rcH7ptVbn968ffVzccuMNTd/2L//mpfHM9S9v+rjzS68t9P73daeeEj+57NKmb3/aK86Ok577onkfq/L5reSXvCMijlg9Er9sEg/db2Qk3vRvn47P/Mv/ju988eK45cYbYvmKFXHUMQ+Lk08/I37/EX/c8tqzd18feqXV+S3K+aXXWp3fxz7j1LjiW1+PGzf+cu4HSKxesyYe8OBj4r+e8sz4wz99TMtrO7/02kLvf0/5m/VxyOFHxM+u+EHcvnlT3Dm2I/ZffZ848piHxROe/bx4yCNPbPq2VT6/lf0MZUTEt67fEreMT3Q1UNqIiPuNLKvsz+KkPJxfMnN+ycz57Vz1voi/h2MPWhXdfiFVo7HrutBrzi+ZOb9k5vx2rtKDcnTZkjhmbXf/x3vo2lUxuqyy3ylAiTi/ZOb8kpnz27lKD8qIXd+rcPSalV251tFr9o/DK/q9D5ST80tmzi+ZOb+dqfT3UO5p49bx2LB5W8zORkffE9GIXZ+mfujaVZU/DJSX80tmzi+ZOb/tqc2gjIgYm5iKyzdti83jE9GI1gdj9+NrR5bFsQdV+9PU5OD8kpnzS2bO78JqNSh3275zMjZuHY+bx3bG2OS+Pwh+dOlwHDy6PI5YPRIHLF86gDuE5pxfMnN+ycz5ba6Wg3JPUzMzsWNiOmZmZ2Oo0YiVy6pZsKeanF8yc37JzPndW+0HJQAAxdR3SgMA0BUGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCEGJQAAhRiUAAAUYlACAFCIQQkAQCH/H1MRoSgXNKmlAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import torch\n",
    "from torch_geometric.data import Data\n",
    "\n",
    "# 生成一个简单的图数据，包括节点特征和位置信息\n",
    "edge_index = torch.tensor([[0, 1, 1, 2, 2, 3, 4, 5],\n",
    "                           [1, 0, 2, 1, 3, 2, 5, 4]], dtype=torch.long)\n",
    "x = torch.tensor([[1.0], [2.0], [3.0], [4.0], [5.0], [6.0]], dtype=torch.float)\n",
    "pos = torch.tensor([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [2.0, 0.0], [2.0, 1.0], [3.0, 0.0]])\n",
    "\n",
    "data = Data(x=x, edge_index=edge_index, pos=pos)\n",
    "\n",
    "# 这时可以使用节点位置信息来可视化图\n",
    "import networkx as nx\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "G = nx.Graph()\n",
    "G.add_nodes_from(range(len(x)))\n",
    "G.add_edges_from(edge_index.t().tolist())\n",
    "\n",
    "pos_dict = {i: pos[i].numpy() for i in range(len(pos))}\n",
    "nx.draw(G, pos_dict, with_labels=True, font_weight='bold', node_color='lightblue')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 异构图"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
